/**
 * @author Vitor Franco do Carmo (vitor.carmo@cpmbraxis.com)
 * @since 18/08/2013
 *
 */
package br.com.vfcfw.commons.util;

import java.math.BigDecimal;
import java.math.RoundingMode;

import org.apache.log4j.Logger;

/**
 * Classe utilitária para manipulação de números.
 * 
 * @author Vitor Franco do Carmo (vitor.carmo@cpmbraxis.com)
 * @since 18/08/2013
 * 
 */
public class NumeroUtil {

    private static final Logger LOGGER = Logger.getLogger(NumeroUtil.class);

    /**
     * Construtor da classe.
     */
    private NumeroUtil() {

        super();
    }

    /**
     * Soma um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param valor1 {@link BigDecimal} Valor 1.
     * @param valor2 {@link BigDecimal} Valor 2.
     * @return {@link BigDecimal} Resultado.
     */
    public static BigDecimal somar(BigDecimal valor1, BigDecimal valor2) {

        return somar(valor1, valor2, 2);
    }

    /**
     * Soma um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param valor1 {@link BigDecimal} Valor 1.
     * @param valor2 {@link BigDecimal} Valor 2.
     * @param precisao Precião.
     * @return {@link BigDecimal} Resultado.
     */
    public static BigDecimal somar(BigDecimal valor1, BigDecimal valor2, int precisao) {

        if (valor1 == null || valor2 == null) {

            LogUtil.error(LOGGER, "Erro ao somar números. Objeto valor 1 e/ou valor 2 nulo.");

            throw new IllegalArgumentException(MensagemUtil.obterMensagem(ConstantesArquitetura.MSG_ERRO_OBJETO_NULO));
        }

        LogUtil.debug(LOGGER, "Somando o valor 1 com o valor 2.");

        return valor1.add(valor2).setScale(precisao);
    }

    /**
     * Subtrai um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param valor1 {@link BigDecimal} Valor 1.
     * @param valor2 {@link BigDecimal} Valor 2.
     * @return {@link BigDecimal} Resultado.
     */
    public static BigDecimal subtrair(BigDecimal valor1, BigDecimal valor2) {

        return subtrair(valor1, valor2, 2);
    }

    /**
     * Subtrai um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param valor1 {@link BigDecimal} Valor 1.
     * @param valor2 {@link BigDecimal} Valor 2.
     * @param precisao Precião.
     * @return {@link BigDecimal} Resultado.
     */
    public static BigDecimal subtrair(BigDecimal valor1, BigDecimal valor2, int precisao) {

        if (valor1 == null || valor2 == null) {

            LogUtil.error(LOGGER, "Erro ao subtrair números. Objeto valor 1 e/ou valor 2 nulo.");

            throw new IllegalArgumentException(MensagemUtil.obterMensagem(ConstantesArquitetura.MSG_ERRO_OBJETO_NULO));
        }

        LogUtil.debug(LOGGER, "Subtraindo o valor 1 com o valor 2.");

        return valor1.subtract(valor2).setScale(precisao);
    }

    /**
     * Multiplica um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param valor1 {@link BigDecimal} Valor 1.
     * @param valor2 {@link BigDecimal} Valor 2.
     * @return {@link BigDecimal} Produto da multiplicação.
     */
    public static BigDecimal multiplicar(BigDecimal valor1, BigDecimal valor2) {

        return multiplicar(valor1, valor2, 2);
    }

    /**
     * Multiplica um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param valor1 {@link BigDecimal} Valor 1.
     * @param valor2 {@link BigDecimal} Valor 2.
     * @param precisao Precião.
     * @return {@link BigDecimal} Produto da multiplicação.
     */
    public static BigDecimal multiplicar(BigDecimal valor1, BigDecimal valor2, int precisao) {

        if (valor1 == null || valor2 == null) {

            LogUtil.error(LOGGER, "Erro ao multiplicar números. Objeto valor 1 e/ou valor 2 nulo.");

            throw new IllegalArgumentException(MensagemUtil.obterMensagem(ConstantesArquitetura.MSG_ERRO_OBJETO_NULO));
        }

        LogUtil.debug(LOGGER, "Multiplicando o valor 1 com o valor 2.");

        return valor1.multiply(valor2).setScale(precisao);
    }

    /**
     * Divide um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param dividendo {@link BigDecimal} Dividendo.
     * @param divisor {@link BigDecimal} Divisor.
     * @return {@link BigDecimal} Resultado da divisão.
     */
    public static BigDecimal dividir(BigDecimal dividendo, BigDecimal divisor) {

        return dividir(dividendo, divisor, 2, RoundingMode.HALF_UP);
    }

    /**
     * Divide um {@link BigDecimal} por outro {@link BigDecimal}.
     * 
     * @param dividendo {@link BigDecimal} Dividendo.
     * @param divisor {@link BigDecimal} Divisor.
     * @param precisao Precião.
     * @param roundingMode {@link RoundingMode} Método de arredondamento.
     * @return {@link BigDecimal} Resultado da divisão.
     */
    public static BigDecimal dividir(BigDecimal dividendo, BigDecimal divisor, int precisao, RoundingMode roundingMode) {

        if (dividendo == null || divisor == null) {

            LogUtil.error(LOGGER, "Erro ao dividir números. Objeto dividendo e/ou divisor nulo.");

            throw new IllegalArgumentException(MensagemUtil.obterMensagem(ConstantesArquitetura.MSG_ERRO_OBJETO_NULO));
        }

        LogUtil.debug(LOGGER, "Dividindo números.");

        return dividendo.divide(divisor, precisao, roundingMode);
    }

    /**
     * Define a precisão de um valor {@link Double}.
     * 
     * @param valor {@link Double} Valor.
     * @param precisao Precisão.
     * @return {@link Double}.
     */
    public static Double definirPrecisao(Double valor, int precisao) {

        return definirPrecisao(valor, precisao, RoundingMode.HALF_UP);
    }

    /**
     * Define a precisão de um valor do tipo {@link Double}.
     * 
     * @param valor {@link Double} Valor.
     * @param precisao Precisão.
     * @param roundingMode {@link RoundingMode} Método de arredondamento.
     * @return {@link Double}.
     */
    public static Double definirPrecisao(Double valor, int precisao, RoundingMode roundingMode) {

        if (valor == null) {

            LogUtil.error(LOGGER, "Erro ao definir a precisão do número. Objeto valor nulo.");

            throw new IllegalArgumentException(MensagemUtil.obterMensagem(ConstantesArquitetura.MSG_ERRO_OBJETO_NULO));
        }

        LogUtil.debug(LOGGER, "Definindo a precisão do número.");

        return BigDecimal.valueOf(valor).setScale(precisao, roundingMode).doubleValue();
    }
}
